package com.wlyuan.open.scanner.repository.impl;

import com.wlyuan.open.constants.CarrierTypeEnum;
import com.wlyuan.open.constants.CustomerTypeEnum;
import com.wlyuan.open.scanner.application.factory.SyncFactory;
import com.wlyuan.open.scanner.assembler.ScannerBillAssembler;
import com.wlyuan.open.scanner.constant.Constants;
import com.wlyuan.open.scanner.dao.entity.BalanceBillindexDO;
import com.wlyuan.open.scanner.dao.mapper.BalanceBillindexMapper;
import com.wlyuan.open.scanner.dao.mapper.FinanceBillMapper;
import com.wlyuan.open.scanner.domain.finance.Bill;
import com.wlyuan.open.scanner.domain.finance.BillConvert;
import com.wlyuan.open.scanner.domain.finance.BillSpec;
import com.wlyuan.open.scanner.domain.transport.Transport;
import com.wlyuan.open.scanner.repository.*;
import com.wlyuan.open.scanner.utils.DateUtils;
import com.wlyuan.open.scanner.utils.StreamUtils;
import com.wlyuan.open.scanner.utils.enums.YesOrNoEnum;
import com.wlyuan.open.scanner.utils.enums.bill.BillStatusEnum;
import com.wlyuan.open.utils.LongUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StopWatch;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author lijing
 * @description
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class BillRepositoryImpl implements BillRepository {
    private final BalanceBillindexMapper balanceBillindexMapper;
    private final CarrierRepository carrierRepository;
    private final FinanceBillRepository financeBillRepository;
    private final DriverRepository driverRepository;
    private final CustomerRepository customerRepository;
    private final BalanceBillDetailRepository balanceBillDetailRepository;
    private final BillTransportRepository billTransportRepository;
    private final SyncFactory syncFactory;
    private final FinanceBillMapper financeBillMapper;
    private final TenantCheckRepository tenantCheckRepository;

    @Override
    public void sync() {
        var count = financeBillMapper.count();
        logger.info("对账单老数据数据量，size：{}", count);
        var pages = (int) Math.ceil(count * 1.0 / Constants.PAGE_SIZE);
        for (int i = 1; i <= pages; i++) {
            logger.info("开始同步对账单数据：第{}页，共{}页", i, pages);
            var list = financeBillMapper.list(i, Constants.PAGE_SIZE);
            list.forEach(data -> {
                try {
                    var ts = parse(data.getId(), data.getUp(), data.getDown(), data.getTenantId());
                    var assemble = ScannerBillAssembler.assemble(ts);
                    syncFactory.sync(assemble);
                } catch (Exception e) {
                    logger.error("对账单同步异常：{},{}", data.getId(), e);
                }
            });
        }
        logger.info("对账单同步完成......");
    }

    @Override
    public List<Bill> list() {
        return Collections.emptyList();
    }

    @Override
    public Bill getById(Long billId) {
        return Optional.ofNullable(billId).map(id -> {
            var finance = financeBillRepository.getByUpOrDown(id);
            return Optional.ofNullable(finance).map(data -> parse(data.getId(), data.getUp(), data.getDown(), data.getTenantId())).orElse(null);
        }).orElse(null);

    }

    @Override
    public List<Long> getTime(String startTime, String endTime) {
        return financeBillRepository.getTime(startTime, endTime);
    }

    @Override
    public List<Long> getTenantIdTime(Long tenantId, String startTime, String endTime) {
        return financeBillRepository.getTenantIdTime(tenantId, startTime, endTime);
    }

    @Override
    public void getByTenantId(Long tenantId) {
        var count = financeBillMapper.countByTenantId(tenantId);
        logger.info("租户{}对账单老数据数据量，size：{}", tenantId, count);
        var pages = (int) Math.ceil(count * 1.0 / Constants.PAGE_SIZE);
        for (int i = 1; i <= pages; i++) {
            logger.info("开始同步租户{}对账单数据：第{}页，共{}页", tenantId, i, pages);
            var list = financeBillMapper.listByTenantId(i, Constants.PAGE_SIZE, tenantId);
            list.forEach(data -> {
                try {
                    var ts = parse(data.getId(), data.getUp(), data.getDown(), data.getTenantId());
                    var assemble = ScannerBillAssembler.assemble(ts);
                    syncFactory.sync(assemble);
                } catch (Exception e) {
                    logger.error("对账单同步异常：{},{}", data.getId(), e);
                }
            });
        }
        logger.info("租户{}对账单同步完成......", tenantId);
    }

    @Override
    public List<Transport> processWayBill(Long billId) {
        var list = balanceBillDetailRepository.getSpecById(billId);
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        var ids = StreamUtils.mapList(list, BillSpec::getTransportId);
        StopWatch watch = new StopWatch();
        watch.start();
        var result = ids.parallelStream().map(id -> billTransportRepository.getBillByTransportId(id)).filter(Objects::nonNull).collect(Collectors.toList());
        watch.stop();
        logger.warn("获取对账单运单信息耗时:{},对账单id：{},运单数据量:{}", watch.getTotalTimeSeconds(), billId, ids.size());
        return result;
    }

    @Override
    public Bill parse(Long id, Long up, Long down, Long tenantId) {
        if (tenantCheckRepository.checkTenant(tenantId)) {
            logger.error("对账单id：{},{},租户id:{}", up, down, tenantId);
            return null;
        }
        if (LongUtils.notNullLong(up) && LongUtils.notNullLong(down)) {
            var data = balanceBillindexMapper.getById(up);
            process(data, id);
            var bill = BillConvert.convert(data, down);
            bill.setWaybills(processWayBill(up));
            bill.setUpStream(BillConvert.upConvert(data));
            //下游
            var downData = balanceBillindexMapper.getById(down);
            process(downData, id);
            bill.setDownStream(BillConvert.downConvert(downData));
            //创建时间
            if (data.getIndexInserttime() != null && downData.getIndexInserttime() != null) {
                bill.setCreateTime(DateUtils.minLocalDateTime(data.getIndexInserttime(), downData.getIndexInserttime()));
            }
            //待审核标识
            if (BillStatusEnum.waitAudit(data.getIndexStatus()) && BillStatusEnum.waitAudit(downData.getIndexStatus())) {
                bill.setWaitAuditFlag(YesOrNoEnum.YES.getValue());
            }
            return bill;
        }
        //线下客户 up = null , down != null  / 线下承运方 up != null , down = null
        var data = LongUtils.notNullLong(up) ? balanceBillindexMapper.getById(up) : balanceBillindexMapper.getById(down);
        process(data, id);
        var bill = BillConvert.convert(data, down);
        bill.setWaybills(processWayBill(data.getIndexId()));
        bill.setDownStream(BillConvert.downConvert(data));
        bill.setUpStream(BillConvert.upConvert(data));
        if (LongUtils.notNullLong(up) && BillStatusEnum.bill(data.getIndexStatus())) {
            Optional.ofNullable(bill.getDownStream()).ifPresent(downStream -> {
                downStream.setStatus(BillStatusEnum.ALREADY_BILL);
            });
        }
        return bill;
    }

    private void process(BalanceBillindexDO data, Long id) {
        //本方待审核
        if (BillStatusEnum.WAIT_CHECK.getValue().equals(data.getIndexStatus()) && LongUtils.notNullLong(data.getIndexSrcid())) {
            data.setIndexStatus(BillStatusEnum.WAIT_ME_CHECK.getValue());
        }
        data.setFbId(id);
        //对账单名称为空处理
        if (StringUtils.isBlank(data.getIndexBname())) {
            data.setIndexBname(defaultBillName(data.getIndexInserttime()));
        }
        if (data.getIndexBillingstatus() != null && data.getIndexBillingstatus() == 1 && data.getIndexBillingdate() == null) {
            data.setIndexBillingdate(data.getIndexInserttime());
            data.setIndexBillingdesc("开票日期为空,系统升级时自动补为对账单制单时间");
        }
        //承运方信息
        processCarrier(data);
        //发货方客户信息
        processCustomer(data);
    }

    @Override
    public void processCustomer(BalanceBillindexDO data) {
        data.setCustomerType(CustomerTypeEnum.MYSELF.getValue());
        data.setDownTenantId(data.getIndexOwnercompanyid());
        if (LongUtils.notNullLong(data.getIndexCustomerid())) {
            var customer = customerRepository.getCustomerById(data.getIndexCustomerid());
            Optional.ofNullable(customer).ifPresent(c -> {
                data.setCustomerType(c.getType());
                data.setCustomerName(c.getName());
                data.setDownTenantId(c.getTenantId());
            });
        }
    }

    /**
     * 承运商
     *
     * @param data
     */
    @Override
    public void processCarrier(BalanceBillindexDO data) {
        //线下客户，承运方是自己
        data.setCarrierType(CarrierTypeEnum.MYSELF.getValue());
        data.setUpTenantId(data.getIndexOwnercompanyid());

        //线上承运方
        onlineSupplier(data);
        //线下承运方
        offlineSupplier(data);
        //自营司机承运方
        driverSupplier(data);
    }

    /**
     * 线上承运方
     */
    private void onlineSupplier(BalanceBillindexDO data) {
        if (LongUtils.notNullLong(data.getIndexSupplierid())) {
            //线上承运商
            var supplier = carrierRepository.getOnlineById(data.getIndexSupplierid());
            Optional.ofNullable(supplier).ifPresent(down -> {
                data.setCarrierId(data.getIndexSupplierid());
                data.setCarrierName(down.getName());
                data.setCarrierContactName(down.getContactName());
                data.setCarrierPhone(down.getContactMobile());
                data.setCarrierType(CarrierTypeEnum.ONLINE.getValue());
                data.setUpTenantId(down.getTenantId());
            });

        }
    }

    /**
     * 线下承运方
     */
    private void offlineSupplier(BalanceBillindexDO data) {
        if (LongUtils.notNullLong(data.getIndexSuppliersymbolid())) {
            //线下
            var symbol = carrierRepository.getOfflineById(data.getIndexSuppliersymbolid());
            Optional.ofNullable(symbol).ifPresent(s -> {
                data.setCarrierType(CarrierTypeEnum.OFFLINE.getValue());
                data.setCarrierId(data.getIndexSuppliersymbolid());
                data.setCarrierName(s.getName());
                data.setCarrierContactName(s.getContactName());
                data.setCarrierPhone(s.getContactMobile());
                data.setUpTenantId(s.getTenantId());
            });
        }
    }

    /**
     * 自营司机承运方
     */
    private void driverSupplier(BalanceBillindexDO data) {
        if (LongUtils.notNullLong(data.getIndexDriverid()) && data.getCarrierId() == null) {
            //自营司机
            var driver = driverRepository.getById(data.getIndexDriverid());
            Optional.ofNullable(driver).ifPresent(d -> {
                data.setCarrierType(CarrierTypeEnum.DRIVER.getValue());
                data.setUpTenantId(d.getTenantId());
            });
        }
    }

    /**
     * 设置对账单名称
     *
     * @param localDateTime
     * @return
     */
    private String defaultBillName(LocalDateTime localDateTime) {
        return DateUtils.chineseMonthDatePattern(localDateTime).concat(Constants.BILL_NAME_SUFFIX);
    }

}
